using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using Sgml;
using System.Xml;
using System.IO;
using Kiiro.Import.WebServices;
using System.Collections;


namespace Kiiro.Import
{

    public enum ImportStage
    {
        ImportStarting,
        ImportingUsers,
        ImportingProject,
        ImportingMessages,
        ImportingFiles,
        ImportingMilestones,
        DownloadingFiles,
        DownloadingFile,
        DownloadingMessages,
        DownloadingMilestones,
        ImportComplete
    }
          

    /// <summary>
    /// All the import settings
    /// </summary>    
    [Serializable]
    public class KiiroImportSettings
    {        
        //General Settings
        public string LocalCacheFolder = "";    //used for downloading files from BC.
        public string ProjectWebServiceURL = "_vti_bin/kiiroproject.asmx";
        public string SecurityWebServiceURL = "_vti_bin/kiirosecurity.asmx";
        public string DiscussionWebServiceURL = "_vti_bin/kiirodiscussion.asmx";
        public string DocumentWebServiceURL = "_vti_bin/kiirodocument.asmx";
        public string TaskWebServiceURL = "_vti_bin/kiirotask.asmx";


        //Basecamp Settings
        public string BasecampURL = "";
        public string BasecampLogin = "";
        public string BasecampPassword = "";
        public bool BasecampSecureMode = false;   

        //Kiiro Settings
        public string KiiroURL = "";

        //User Import Options - this will be implemented later
        //public bool ImportUsers = false;
        //public bool MapToAD = false;
        //public string ADServer = "";
        //public string ADDomain = "";
        //public string FormsPassword = "";        

        //Project Import options
        public bool ImportMessages = false;
        public bool ImportFiles = false;
        public bool ImportMilestones = false;
        public bool DeleteExistingProjects = false;
        public bool ReuseFiles = false;

        public KiiroUser DefaultKiiroUser;

        public IList<BasecampProject> ProjectsToImport = null;

        public IList<KiiroUser> ActiveKiiroUsers = null;

        public Hashtable BlockedFileExtensions = null;

        //import log
        public string ImportLog = "";
        public bool Success = false;

        //URL which takes into account the secure access mode
        public string BasecampFullURL
        {
            get
            {                
                if (BasecampSecureMode)
                    return "https://" + BasecampURL;
                else
                    return "http://" + BasecampURL;     
            }         
        }

        public KiiroImportSettings()
        {

        }

        private string BoolToYesNo(bool value)
        {
            if (value == true)
                return ("Yes");
            else
                return ("No");

        }

        public override string ToString()
        {            
            string summary = "Basecamp URL: " + BasecampURL;
            summary += "\r\nBasecamp Login: " + BasecampLogin;
            summary += "\r\nKiiro URL: " + KiiroURL;
            summary += "\r\nImport Messages: " + BoolToYesNo(ImportMessages);
            summary += "\r\nImport Files: " + BoolToYesNo(ImportFiles);
            summary += "\r\nImport Milestones: " + BoolToYesNo(ImportMilestones);
            if (DefaultKiiroUser != null)
                summary += "\r\nDefault Kiiro User: " + DefaultKiiroUser.Name;
            summary += "\r\nDelete Existing Projects in Kiiro: " + BoolToYesNo(DeleteExistingProjects);
            summary += "\r\nRe-use Previously Downloaded Files: " + BoolToYesNo(ReuseFiles);
            summary += "\r\nProjects to Import:";
            if (ProjectsToImport != null)
            {
                foreach (BasecampProject proj in ProjectsToImport)
                {
                    summary += "\r\n\t" + proj.CompanyName + " - " + proj.ProjectName;
                }
            }
            return summary;
        }

    }

    /// <summary>
    /// Handles the issue that WebClient does not expose cookies.
    /// </summary>
    public class CookieAwareWebClient : WebClient
    {
        public CookieContainer Cookies = null;

        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest request = base.GetWebRequest(address);
            if (request is HttpWebRequest)
            {
                (request as HttpWebRequest).CookieContainer = Cookies;
            }
            return request;
        }
    }

    /// <summary>
    /// Describes a Basecamp project
    /// </summary>
    [Serializable]
    public class BasecampProject
    {
        public string ID = "";
        public string ProjectName = "";
        public string CompanyName = "";
        public DateTime CreatedDate;
        public DateTime LastUpdatedDate;
        public string Status = "";      

        public IList<BasecampFile> ProjectFiles = null;
        public IList<BasecampPost> ProjectPosts = null;
        public IList<BasecampPerson> ProjectPeople = null;
        public IList<BasecampMilestone> ProjectMilestones = null;

        public BasecampProject(string id, string projectName, string companyName, DateTime createdDate, DateTime lastUpdatedDate, string status)
        {
            ID = id;
            ProjectName = projectName;
            CompanyName = companyName;
            CreatedDate = createdDate;
            LastUpdatedDate = lastUpdatedDate;
            Status = status;
        }
    }

    /// <summary>
    /// Describes a Basecamp project
    /// </summary>
    [Serializable]
    public class BasecampFile
    {
        public string URL= "";
        public string Name = "";
        public string LocalPath = "";
        public string MessageID = "";
        public string CommentID = "";
        public DateTime CreatedDate = DateTime.Now;
        public string CreatedByName = "";
        public string KiiroSessionKey = "";
        public KiiroUser KiiroCreatedBy = null;
        //public List<BasecampFileVersion> Versions = null;

        public BasecampFile(string url, string name, DateTime createdDate, string createdByName)
        {
            URL = url;
            Name = name;
            CreatedDate = createdDate;
            CreatedByName = createdByName;
        }
        public BasecampFile(string url, string name, DateTime createdDate, string createdByName, string localPath)
        {
            URL = url;
            Name = name;
            LocalPath = localPath;
            CreatedDate = createdDate;
            CreatedByName = createdByName;
            
        }
    }

    ///// <summary>
    ///// Describes a Basecamp project
    ///// </summary>
    //[Serializable]
    //public class BasecampFileVersion : BasecampFile
    //{
    //    public int Version = 0;

    //    public BasecampFileVersion(string url, string name, DateTime createdDate, string createdByName, int version)
    //    {
    //        URL = url;
    //        Name = name;
    //        CreatedDate = createdDate;
    //        CreatedByName = createdByName;
    //        Version = version;
    //    }

    //    public BasecampFileVersion(string url, string name, DateTime createdDate, string createdByName, string localPath, int version)
    //    {
    //        URL = url;
    //        Name = name;
    //        LocalPath = localPath;
    //        CreatedDate = createdDate;
    //        CreatedByName = createdByName;
    //        Version = version;
    //    }
    //}

    [Serializable]
    public class BasecampPost
    {
        public string ID = "";
        public string Title = "";
        public string Body = "";
        public string CreatedByID = "";
        public DateTime CreatedDate = DateTime.Now;
        public KiiroUser KiiroCreatedBy = null;

        public IList<BasecampFile> Files = null;
        public IList<BasecampComment> Comments = null;

        public BasecampPost(string id, string title, string body, string createdByID, DateTime createdDate)
        {
            ID = id;
            Title = title;
            Body = body;
            CreatedByID = createdByID;
            CreatedDate = createdDate;
        }

    }

    [Serializable]
    public class BasecampComment
    {
        public string ID = "";
        public string Body = "";
        public string CreatedByID = "";
        public KiiroUser KiiroCreatedBy = null;
        public DateTime CreatedDate = DateTime.Now;
        public IList<BasecampFile> Files = null;

        public BasecampComment(string id, string body, string createdByID, DateTime createdDate)
        {
            ID = id;
            Body = body;
            CreatedByID = createdByID;
            CreatedDate = createdDate;
        }

    }

    [Serializable]
    public class BasecampMilestone
    {
        public string ID = "";
        public string Title = "";
        public bool Completed = false;
        public DateTime DueDate = DateTime.MaxValue;
        public string ResponsibleID = "";
        public string CreatedByID = "";
        public KiiroUser KiiroCreatedBy = null;
        public KiiroUser KiiroResponsible = null;
        public KiiroUser KiiroCompletedBy = null;
        public DateTime CreatedDate = DateTime.MaxValue;
        public DateTime CompletedDate = DateTime.MaxValue;
        

        public BasecampMilestone(string id, string title, DateTime dueDate, bool completed, DateTime completedDate, string responsibleID, string createdByID, DateTime createdDate)
        {
            ID = id;
            Title = title;
            Completed = completed;
            ResponsibleID = responsibleID;
            CreatedByID = createdByID;
            CreatedDate = createdDate;
            DueDate = dueDate;
            CompletedDate = completedDate;
        }

    }

    [Serializable]
    public class BasecampPerson
    {
        public string ID = "";
        public string FirstName = "";
        public string LastName = "";
        public string Title = "";
        public string Email = "";
        public KiiroUser KiiroUser = null;

        public IList<BasecampFile> Files = null;

        public BasecampPerson(string id, string firstName, string lastName, string title, string email)
        {
            ID = id;
            FirstName = firstName;
            LastName = lastName;
            Title = title;
            Email = email;
        }

    }

    [Serializable]
    public class KiiroUser
    {
        public int ID = 0;
        public string Name = "";
        public string Email = "";
        public bool CanCreateProjects = false;

        public KiiroUser(int id, string name, string email, bool canCreateProjects)
        {
            ID = id;
            Name = name;
            Email = email;
            CanCreateProjects = canCreateProjects;
        }

        public override string ToString() 
        {
            return Name;
        }

    }



    public class KiiroBasecampImport : IDisposable
    {
        
        
        //Delegate for getting updates on the import status
        public delegate void ImportStatusHandler(ImportStage stage, int current, int total, int percentComplete, string description);
        public event ImportStatusHandler ImportStatusChange;

        //Delegate for detailed log
        public delegate void SystemLogHandler(string message);
        public event SystemLogHandler SystemLogChange;

        //updated with the progress of a file download
        private int downloadProgress = 0;
        
        ///keeps the session cookies for basecamp
        private CookieContainer BasecampSession = null;

        /// <summary>
        /// helper method which logs a message
        /// </summary>
        private void WriteToSystemLog(string message)
        {
            if (SystemLogChange != null)
                SystemLogChange(message);
        }


        private void CreateProject(BasecampProject project, KiiroImportSettings importSettings, out KiiroProjectStatusFlags status)
        {
            string teamMemberIDs = "";

            if (importSettings.KiiroURL.Substring(importSettings.KiiroURL.Length - 1, 1) != @"/")
                importSettings.KiiroURL += @"/";

            WebServices.KiiroProject projectWS = new WebServices.KiiroProject();
            projectWS.Credentials = CredentialCache.DefaultCredentials;
            projectWS.Url = importSettings.KiiroURL + importSettings.ProjectWebServiceURL;
            status = null;

            status = projectWS.CreateProject(project.ProjectName, project.ID.ToString(), importSettings.DefaultKiiroUser.ID, project.CreatedDate, project.LastUpdatedDate, importSettings.DeleteExistingProjects);

            
            if (status.ExceptionOccurred)
                throw new Exception("An exception occurred creating the project in Kiiro: " + status.Exception);
            else if (status.UserDoesNotExist)
                throw new Exception("The default user selected no longer exists. Select a different user.");                        
            else
            {
                if (status.ProjectURLAlreadyExists)
                    RecordImportLog("Project already exists in Kiiro, creation skipped.", importSettings, true);
                else
                {
                    //loop through and add people to the team
                    foreach (BasecampPerson person in project.ProjectPeople)
                    {
                        if (person.KiiroUser != null)
                        {
                            if (teamMemberIDs != "")
                                teamMemberIDs += ",";
                            teamMemberIDs += person.KiiroUser.ID.ToString();
                        }
                    }
                    if (teamMemberIDs != "")
                    {
                        status = projectWS.AddTeamMembersToProject(project.ID.ToString(), importSettings.DefaultKiiroUser.ID, teamMemberIDs);
                        if (status.ExceptionOccurred)
                            throw new Exception("An exception occurred adding the team to the new project in Kiiro: " + status.Exception);
                    }
                }
            }
        }

        private void ImportDiscussions(BasecampProject project, KiiroImportSettings importSettings)
        {

            KiiroDiscussionStatusFlags status = null;
            string[] sessionKeys = null;
            string[] fileNames = null;
            int discussionID = 0;
            int count = 0;

            importSettings.Success = true;

            if (importSettings.KiiroURL.Substring(importSettings.KiiroURL.Length - 1, 1) != @"/")
                importSettings.KiiroURL += @"/";

            WebServices.KiiroDiscussion discussionWS = new WebServices.KiiroDiscussion();
            discussionWS.Credentials = CredentialCache.DefaultCredentials;
            discussionWS.Url = importSettings.KiiroURL + importSettings.DiscussionWebServiceURL;
            status = null;

            RecordImportLog("Discussions to import : " + project.ProjectPosts.Count.ToString(), importSettings, true);

            foreach(BasecampPost discussion in project.ProjectPosts)
            {

                ++count;

                NotifyStatusChange(ImportStage.ImportingMessages, count, project.ProjectPosts.Count);

                //upload any attachments to the server
                UploadMultipleFiles(discussion.Files, importSettings);
                GetSessionKeyArrays(discussion.Files, out sessionKeys, out fileNames);
                discussionID = discussionWS.CreateDiscussion(project.ID.ToString(), discussion.Title, discussion.Body, discussion.CreatedDate, discussion.KiiroCreatedBy.ID, sessionKeys, fileNames, out status);
                
                if (status.ExceptionOccurred)
                    throw new Exception("An exception occurred creating the discussion '" + discussion.Title + "' in Kiiro: " + status.Exception);
                else if (status.Success == false)
                {
                    importSettings.Success = false;
                    RecordImportLog("Discussion not imported '" + discussion.Title + "'", importSettings);
                    break;
                }
                else
                {   
                    //import each comment into Kiiro
                    foreach (BasecampComment comment in discussion.Comments)
                    {
                        UploadMultipleFiles(comment.Files, importSettings);
                        GetSessionKeyArrays(comment.Files, out sessionKeys, out fileNames);
                        status = discussionWS.AddCommentToDiscussion(project.ID.ToString(), discussionID, comment.Body, comment.CreatedDate, comment.KiiroCreatedBy.ID, sessionKeys, fileNames);

                        if (status.ExceptionOccurred)
                            throw new Exception("An exception occurred creating a discussion comment '" + comment.Body + "' in Kiiro: " + status.Exception);
                        else if (status.Success == false)
                        {
                            importSettings.Success = false;
                            RecordImportLog("Discussion comment not imported '" + comment.Body + "'", importSettings);
                            break;
                        }

                    }

                }                
            }
        }

        private void ImportTasks(BasecampProject project, KiiroImportSettings importSettings)
        {

            KiiroTaskStatusFlags status = null;
            int count = 0;
            int taskID = 0;
            int percentComplete = 0;
            importSettings.Success = true;

            if (importSettings.KiiroURL.Substring(importSettings.KiiroURL.Length - 1, 1) != @"/")
                importSettings.KiiroURL += @"/";

            WebServices.KiiroTask taskWS = new WebServices.KiiroTask();
            taskWS.Credentials = CredentialCache.DefaultCredentials;
            taskWS.Url = importSettings.KiiroURL + importSettings.TaskWebServiceURL;
            status = null;
            int modifiedBy = 0;
            DateTime modifiedDate;

            foreach (BasecampMilestone milestone in project.ProjectMilestones)
            {

                ++count;

                NotifyStatusChange(ImportStage.ImportingMilestones, count, project.ProjectMilestones.Count);

                if (milestone.Completed)
                    percentComplete = 100;
                else
                    percentComplete = 0;

                if (milestone.Completed)
                {
                    modifiedBy = milestone.KiiroResponsible.ID;
                    modifiedDate = milestone.CompletedDate;
                }
                else
                {
                    modifiedBy = milestone.KiiroCreatedBy.ID;
                    modifiedDate = milestone.CreatedDate;
                }

                taskID = taskWS.CreateTask(project.ID.ToString(), milestone.Title, percentComplete, milestone.DueDate, milestone.CreatedDate, modifiedDate, milestone.KiiroCreatedBy.ID, modifiedBy, milestone.KiiroResponsible.ID, out status);

                if (status.ExceptionOccurred)
                    throw new Exception("An exception occurred creating the task '" + milestone.Title + "' in Kiiro: " + status.Exception);
                else if (status.Success == false)
                {
                    importSettings.Success = false;
                    RecordImportLog("Task not imported '" + milestone.Title + "'", importSettings);
                    break;
                }                
            }
        }

        private void GetSessionKeyArrays(IList<BasecampFile> files, out string[] keys, out string [] fileNames)
        {
            keys = null;
            fileNames = null;
            if (files != null)
            {
                keys = new string[files.Count];
                fileNames = new string[files.Count];
                int i = 0;

                foreach (BasecampFile file in files)
                {
                    keys[i] = file.KiiroSessionKey;
                    fileNames[i] = file.Name.Replace("#","_").Replace("%", "_").Replace("&", "_").Replace(":","_").Replace("<","_").Replace(">","_").Replace("?","_").Replace("/","_").Replace("{","_").Replace("|","_").Replace("}","_");
                    ++i;
                }
            }
        }

        private void ImportDocuments(BasecampProject project, KiiroImportSettings importSettings)
        {

            //load all the documents that are not part of discussions
            KiiroDocumentStatusFlags status = null;
            int count = 0;
            int totalCount = 0;
            IList<BasecampFile> filesToUpload = new List<BasecampFile>();

            importSettings.Success = true;

            if (importSettings.KiiroURL.Substring(importSettings.KiiroURL.Length - 1, 1) != @"/")
                importSettings.KiiroURL += @"/";

            WebServices.KiiroDocument documentWS = new WebServices.KiiroDocument();
            documentWS.Credentials = CredentialCache.DefaultCredentials;
            documentWS.Url = importSettings.KiiroURL + importSettings.DocumentWebServiceURL;
            status = null;

            //get the total count to upload for our status notification
            totalCount= 0;
            foreach (BasecampFile document in project.ProjectFiles)
            {
                if (document.CommentID == "" && document.MessageID == "")
                    ++totalCount;
            }

            RecordImportLog("Documents to import not associated with messages : " + totalCount.ToString(), importSettings, true);

            //upload the docs
            foreach (BasecampFile document in project.ProjectFiles)
            {
                //only import non-message/comment documents
                if (document.CommentID == "" && document.MessageID == "")
                {
                    ++count;

                    NotifyStatusChange(ImportStage.ImportingFiles, count, totalCount);

                    if (!IsFileExtensionAllowed(document.LocalPath, importSettings))
                    {
                        RecordImportLog("File extension not supported in Sharepoint. Document skipped: '" + document.Name + "'", importSettings, true);
                    }
                    else
                    {

                        //upload the document to the server
                        filesToUpload.Clear();
                        filesToUpload.Add(document);
                        UploadMultipleFiles(filesToUpload, importSettings);
                        
                        //add the document to the library
                        status = documentWS.AddDocumentToLibrary(project.ID.ToString(), document.KiiroSessionKey, document.Name, document.KiiroCreatedBy.ID, document.CreatedDate);
                        
                        if (status.ExceptionOccurred)
                            throw new Exception("An exception occurred importing the document '" + document.Name + "' in Kiiro: " + status.Exception);
                        else if (status.Success == false)
                        {
                            importSettings.Success = false;
                            RecordImportLog("Document not imported '" + document.Name + "'", importSettings);
                            break;
                        }
                    }
                }
            }
        }

        private void GetNextDocumentChunk(string path, int chunkSize, out byte[] documentChunk, out bool finished, ref FileStream handle)
        {

            if (handle == null)
                handle = new FileStream(path, FileMode.Open, FileAccess.Read);

            finished = false;

            if ((handle.Position + chunkSize) > handle.Length)
                chunkSize = (Int32)(handle.Length - handle.Position);

            documentChunk = new byte[chunkSize];

            if (handle.Read(documentChunk, 0, chunkSize) == 0)
            {
                finished = true;
                handle.Close();
            }
        }

        private void UploadMultipleFiles(IList<BasecampFile> documents, KiiroImportSettings importSettings)
        {

            WebServices.KiiroDocument documentWS = new WebServices.KiiroDocument();
            documentWS.Credentials = CredentialCache.DefaultCredentials;
            documentWS.Url = importSettings.KiiroURL + importSettings.DocumentWebServiceURL;
            KiiroDocumentStatusFlags status = null;
            
            //upload the docs
            if (documents != null)
            {
                foreach (BasecampFile document in documents)
                {

                    //upload the document in chunks
                    string sessionKey = "";
                    byte[] documentChunk = null;
                    bool finished = false;
                    FileStream handle = null;

                    do
                    {
                        GetNextDocumentChunk(document.LocalPath, 65536, out documentChunk, out finished, ref handle);

                        status = documentWS.TransferDocumentChunk(ref sessionKey, documentChunk);

                        if (!status.Success)
                            break;

                    } while (finished == false);

                    if (status.ExceptionOccurred)
                        throw new Exception("An exception occurred importing the document '" + document.Name + "' in Kiiro: " + status.Exception);
                    else if (status.Success == false)
                    {
                        importSettings.Success = false;
                        RecordImportLog("Document not imported '" + document.Name + "'", importSettings);
                    }
                    else
                    {
                        //this doc is sitting on the server, record the session key
                        document.KiiroSessionKey = sessionKey;
                    }
                }
            }
        }

        private KiiroUser MatchBasecampUserToKiiroByID(string basecampUserID, BasecampProject project, KiiroImportSettings importSettings)
        {
            KiiroUser user = importSettings.DefaultKiiroUser;

            foreach (BasecampPerson person in project.ProjectPeople)
            {
                if (person.ID == basecampUserID)
                {
                    if (person.KiiroUser != null)
                    {
                        user = person.KiiroUser;
                        break;
                    }
                }
            }

            return user;
        }

        private KiiroUser MatchBasecampUserToKiiroByName(string basecampUserName, BasecampProject project, KiiroImportSettings importSettings)
        {
            KiiroUser user = importSettings.DefaultKiiroUser;

            foreach (BasecampPerson person in project.ProjectPeople)
            {
                if (person.FirstName + " " + person.LastName == basecampUserName)
                {
                    if (person.KiiroUser != null)
                    {
                        user = person.KiiroUser;
                        break;
                    }
                }
            }

            return user;
        }


        //public void AddUserTest(string kiiroURL, string domain, string login)
        //{
        //    if (kiiroURL.Substring(kiiroURL.Length - 1, 1) != @"/")
        //        kiiroURL += @"/";

        //    WebServices.KiiroSecurity ws = new WebServices.KiiroSecurity();
        //    ws.Credentials = CredentialCache.DefaultCredentials;
        //    ws.Url = kiiroURL + SecurityWebServiceURL;
        //    WebServices.KiiroSecurityStatusFlags status = null;

        //    ws.AddADUser(domain, login,"NFORM-TOBYS-WSS\\administrator", status);

        //}

        /// <summary>
        /// Gets a list of the projects in BaseCamp that the user has access to.
        /// </summary>
        public IList<BasecampProject> GetBasecampProjectList(KiiroImportSettings importSettings)
        {
            BasecampManager manager = new BasecampManager(importSettings.BasecampURL, importSettings.BasecampLogin, importSettings.BasecampPassword, importSettings.BasecampSecureMode);
           
            IList<Project> projects = new List<Project>();
            IList<BasecampProject> basecampProjects = new List<BasecampProject>();

            projects = manager.GetProjects();

            foreach (Project prj in projects)
            {
                basecampProjects.Add(new BasecampProject(prj.ID.ToString(), prj.Name, prj.Company.Name, prj.Created, prj.LastChanged, prj.Status));
            }

            return basecampProjects;
            
        }      

        /// <summary>
        /// Tests connectivity to basecamp
        /// </summary>
        public bool TestKiiroConnection(KiiroImportSettings importSettings)
        {
            bool kiiroOK = false;

            //send an http request to the Kiiro project web services
            WebRequest request = WebRequest.Create(importSettings.KiiroURL + importSettings.ProjectWebServiceURL); 
            request.Credentials = CredentialCache.DefaultCredentials;
            try
            {
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    XmlDocument doc = ConvertResponseToXML(response, importSettings);
                    
                    //see if the title is an error
                    XmlNodeList nodes = doc.SelectNodes("//head/title");
                    if (!nodes[0].InnerText.ToUpper().Contains("ERROR"))
                        kiiroOK = true;
                }
            }
            catch (Exception ex)
            {
                ;                
            }
            return kiiroOK;

        }

        

        /// <summary>
        /// Tests connectivity to basecamp
        /// </summary>
        public bool TestBasecampConnection(KiiroImportSettings importSettings)
        {
            bool success = false;
   
            //clear the login cache
            BasecampSession = null;

            //try and login
            try
            {            
                if (LoginToBasecamp(importSettings))
                    success = true;
            }
            catch (Exception ex) {}

            return success;

        }


        public IList<KiiroUser> GetKiiroUserList(KiiroImportSettings importSettings)
        {

            KiiroUserSummary[] wsUserList = null;
            IList<KiiroUser> returnUserList = new List<KiiroUser>();
            WebServices.KiiroSecurity ws = new WebServices.KiiroSecurity();
            ws.Credentials = CredentialCache.DefaultCredentials;
            ws.Url = importSettings.KiiroURL + importSettings.SecurityWebServiceURL;
            WebServices.KiiroSecurityStatusFlags status = null;

            wsUserList = ws.GetActiveUserList(status);

            foreach(KiiroUserSummary wsUser in wsUserList)
            {
                returnUserList.Add(new KiiroUser(wsUser.ID, wsUser.Name, wsUser.Email, wsUser.CanCreateProjects));
            }            
            return (returnUserList);
        }

        /// <summary>
        /// Tests connectivity to AD
        /// </summary>
        public bool TestActiveDirectoryConnection(KiiroImportSettings importSettings)
        {
            return true;

        }

        
        /// <summary>
        /// Imports the given projects into Kiiro
        /// </summary>
        public void ImportToKiiro(KiiroImportSettings importSettings)
        {

            int currentProject = 0;
            importSettings.Success = false;
            KiiroProjectStatusFlags projectStatus = null;            
            
            try
            {

                //string name = GetCorrectedFileName(@"C:\KiiroBasecampImport\ImportTestHarness\bin\Debug\ProjectFileCache\2729741\", "0123456789012345678901234567890123456789012345 extra.rtf", importSettings);

                NotifyStatusChange(ImportStage.ImportStarting);
                RecordImportLog("Import started at " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString(), importSettings);
                RecordImportLog("============================================================", importSettings);
                RecordImportLog("Current user: " + System.Security.Principal.WindowsIdentity.GetCurrent().Name, importSettings);                
                RecordImportLog(importSettings.ToString(), importSettings);
                RecordImportLog("============================================================", importSettings);

                foreach (BasecampProject project in importSettings.ProjectsToImport)
                {

                    ++currentProject;

                    NotifyStatusChange(ImportStage.ImportingProject, currentProject, importSettings.ProjectsToImport.Count, project.CompanyName + " - " + project.ProjectName);
                    RecordImportLog("Importing project: " + project.CompanyName + " - " + project.ProjectName, importSettings);
                    
                    //Get the project team and match to Kiiro active users using email
                    ExportProjectTeam(project, importSettings);                    

                    //create the project and team in Kiiro
                    RecordImportLog("Creating project in Kiiro.", importSettings, true);
                    CreateProject(project, importSettings, out projectStatus);

                   
                    //get the files for the project from Basecamp. We need to files even if only importing
                    //messages so that we have the attachments.
                    if (importSettings.ImportMessages || importSettings.ImportFiles)
                    {
                        NotifyStatusChange(ImportStage.DownloadingFiles);
                        RecordImportLog("Exporting files from Basecamp.", importSettings, true);
                        ExportFiles(project, importSettings);
                    }
                   

                    //Import files not associated with messages or comments. These go straight to the doc library
                    if (importSettings.ImportFiles)
                    {
                        NotifyStatusChange(ImportStage.ImportingFiles);
                        RecordImportLog("Importing files into Kiiro as documents.", importSettings, true);
                        ImportDocuments(project, importSettings);
                    }

                    //get the milestones for the project from Basecamp
                    if ((importSettings.ImportMilestones))
                    {
                        NotifyStatusChange(ImportStage.DownloadingMilestones);
                        RecordImportLog("Exporting milestones from Basecamp.", importSettings, true);
                        ExportMilestones(project, importSettings);

                        //Import tasks
                        NotifyStatusChange(ImportStage.ImportingMilestones);
                        RecordImportLog("Importing milestones into Kiiro as tasks.", importSettings, true);
                        ImportTasks(project, importSettings);
                    }

                    //get the messages for the project from Basecamp
                    if (importSettings.ImportMessages)
                    {
                        NotifyStatusChange(ImportStage.DownloadingMessages);
                        RecordImportLog("Exporting messages from Basecamp.", importSettings, true);
                        ExportPosts(project, importSettings);

                        //import the messages into Kiiro and include attachments on messages and comments.
                        NotifyStatusChange(ImportStage.ImportingMessages);
                        RecordImportLog("Importing messages into Kiiro as discussions.", importSettings, true);
                        ImportDiscussions(project, importSettings);
                    }                                               

                }

                //Complete the import
                NotifyStatusChange(ImportStage.ImportComplete);

                importSettings.Success = true;

                RecordImportLog("============================================================", importSettings);
                RecordImportLog("Import completed at " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString(), importSettings);
                RecordImportLog("============================================================", importSettings);

            }
            catch (Exception ex)
            {
                importSettings.Success = false;
                RecordImportLog("", importSettings);
                RecordImportLog("Exception occurred during import:", importSettings);
                RecordImportLog("", importSettings);
                RecordImportLog(ex.ToString(), importSettings);
            }

        }


        private void ExportProjectTeam(BasecampProject project, KiiroImportSettings importSettings)
        {
            BasecampManager manager = new BasecampManager(importSettings.BasecampURL, importSettings.BasecampLogin, importSettings.BasecampPassword, importSettings.BasecampSecureMode);
            project.ProjectPeople = new List<BasecampPerson>();
            IList<Person> people = new List<Person>();            
                        
            people = manager.GetPeople(Int32.Parse(project.ID));

            //get kiiro users if not already in the settings
            //if (importSettings.ActiveKiiroUsers == null)
                importSettings.ActiveKiiroUsers = GetKiiroUserList(importSettings);

            //add the people to the project team
            foreach (Person person in people)
            {
                string phone = person.phoneNumberOffice;
                if (person.PhoneNumberOfficeExt != null && phone != null && person.PhoneNumberOfficeExt!= "" && phone != "")
                    phone += "x" + person.PhoneNumberOfficeExt;                    

                project.ProjectPeople.Add(new BasecampPerson(person.ID.ToString(), person.FirstName, person.LastName, person.Title, person.Email));

                //try and match this person with our active user list
                foreach (KiiroUser user in importSettings.ActiveKiiroUsers)
                {
                    if (user.Email != null)
                    {
                        if (user.Email.ToLower() == person.Email.ToLower())
                        {
                            project.ProjectPeople[project.ProjectPeople.Count - 1].KiiroUser = user;
                            break;
                        }
                    }
                }

            }        
        }

        private void ExportMilestones(BasecampProject project, KiiroImportSettings importSettings)
        {
            BasecampManager manager = new BasecampManager(importSettings.BasecampURL, importSettings.BasecampLogin, importSettings.BasecampPassword, importSettings.BasecampSecureMode);
            project.ProjectMilestones = new List<BasecampMilestone>();
            IList<Milestone> milestones = new List<Milestone>();
            int count=0;

            milestones = manager.GetMilestones(Int32.Parse(project.ID));

            NotifyStatusChange(ImportStage.DownloadingMilestones, 0, milestones.Count);

            //add the milestones
            foreach (Milestone milestone in milestones)
            {
                project.ProjectMilestones.Add(new BasecampMilestone(milestone.ID.ToString(), milestone.Title, milestone.Due, milestone.Completed, milestone.CompletedOn, milestone.ResponsiblePartyID.ToString(), milestone.CreatedByPersonID.ToString(), milestone.Created));
                project.ProjectMilestones[project.ProjectMilestones.Count - 1].KiiroCreatedBy = MatchBasecampUserToKiiroByID(milestone.CreatedByPersonID.ToString(), project, importSettings);
                project.ProjectMilestones[project.ProjectMilestones.Count - 1].KiiroResponsible = MatchBasecampUserToKiiroByID(milestone.ResponsiblePartyID.ToString(), project, importSettings);

                if (milestone.Completed)
                    project.ProjectMilestones[project.ProjectMilestones.Count - 1].KiiroCompletedBy = MatchBasecampUserToKiiroByID(milestone.CreatedByPersonID.ToString(), project, importSettings);

                ++count;
                NotifyStatusChange(ImportStage.DownloadingMilestones, count, milestones.Count);
            }
        }

        private void ExportPosts(BasecampProject project, KiiroImportSettings importSettings)
        {
            BasecampManager manager = new BasecampManager(importSettings.BasecampURL, importSettings.BasecampLogin, importSettings.BasecampPassword, importSettings.BasecampSecureMode);
            project.ProjectPosts = new List<BasecampPost>();
            IList<AbbreviatedPost> abbreviatedPosts = new List<AbbreviatedPost>();
            IList<Post> posts = new List<Post>();
            IList<Comment> comments = new List<Comment>();
            String messageIDs = "";
            int messageCount = 0;
            importSettings.Success = true;


            abbreviatedPosts = manager.GetMessageArchiveByProject(Int32.Parse(project.ID));

            NotifyStatusChange(ImportStage.DownloadingMessages, 0, abbreviatedPosts.Count);
            RecordImportLog("Message count in Basecamp determined to be : " + abbreviatedPosts.Count.ToString(), importSettings, true);

            //loop through each post and get the full post from the server in batches of 10.
            foreach (AbbreviatedPost abbreviatedPost in abbreviatedPosts)
            {               

                ++messageCount;
                if (messageIDs != "")
                    messageIDs += ",";
                messageIDs += abbreviatedPost.ID.ToString();

                if ((messageCount % 25) == 0 || project.ProjectPosts.Count + messageCount == abbreviatedPosts.Count)
                {
                    //load this batch of posts and add them to the project
                    posts = manager.GetMessages(messageIDs);

                    NotifyStatusChange(ImportStage.DownloadingMessages, project.ProjectPosts.Count + messageCount, abbreviatedPosts.Count);

                    foreach (Post post in posts)
                    {

                        project.ProjectPosts.Add(new BasecampPost(post.ID.ToString(), post.Title, post.Body, post.AuthorID.ToString(), post.Posted));
                        project.ProjectPosts[project.ProjectPosts.Count - 1].KiiroCreatedBy = MatchBasecampUserToKiiroByID(post.AuthorID.ToString(), project, importSettings);

                        //get the comments for this post
                        comments = null;
                        project.ProjectPosts[project.ProjectPosts.Count - 1].Comments = new List<BasecampComment>();
                        if (post.CommentsCount > 0)
                        {
                            comments = manager.GetComments(post.ID);                            
                            foreach(Comment comment in comments)
                            {
                                project.ProjectPosts[project.ProjectPosts.Count-1].Comments.Add(new BasecampComment(comment.ID.ToString(), comment.Body, comment.AuthorID.ToString(), comment.PostedOn));
                                project.ProjectPosts[project.ProjectPosts.Count - 1].Comments[project.ProjectPosts[project.ProjectPosts.Count - 1].Comments.Count - 1].KiiroCreatedBy = MatchBasecampUserToKiiroByID(comment.AuthorID.ToString(), project, importSettings);
                            }
                        }

                        //link up the downloaded files to the post
                        LinkFilesToPost(project.ProjectPosts[project.ProjectPosts.Count - 1], project);
                        
                    }
                    
                    messageIDs = "";
                    messageCount = 0;
                }
                //break;
            }

        }

        private void LinkFilesToPost(BasecampPost post, BasecampProject project)
        {
            //find any files that are for this post or its comments            
            post.Files = new List<BasecampFile>();
            if (project.ProjectFiles != null)
            {
                foreach (BasecampFile file in project.ProjectFiles)
                {
                    if (file.MessageID == post.ID)
                    {
                        post.Files.Add(file);
                    }
                }

                foreach (BasecampComment comment in post.Comments)
                {
                    comment.Files = new List<BasecampFile>();
                    foreach (BasecampFile file in project.ProjectFiles)
                    {
                        if (file.CommentID == comment.ID)
                        {
                            comment.Files.Add(file);
                        }
                    }
                }

            }
        }

        private void RecordImportLog(string message, KiiroImportSettings importSettings)
        {
            RecordImportLog(message, importSettings, false);
        }


        private void RecordImportLog(string message, KiiroImportSettings importSettings, bool indent)
        {
            if (indent)
                importSettings.ImportLog += "\t";

            
            importSettings.ImportLog += message + "\r\n";
        }

        private void NotifyStatusChange(ImportStage stage)
        {
            if (ImportStatusChange != null)
                ImportStatusChange(stage, 0, 0, 0, "");
        }

        private void NotifyStatusChange(ImportStage stage, string description)
        {
            if (ImportStatusChange != null)
                ImportStatusChange(stage, 0, 0, 0, description);
        }

        private void NotifyStatusChange(ImportStage stage, int current, int total)
        {
            if (ImportStatusChange != null)
                ImportStatusChange(stage, current, total, 0, "");
        }


        private void NotifyStatusChange(ImportStage stage, int current, int total, string description)
        {
            if (ImportStatusChange != null)
                ImportStatusChange(stage, current, total, 0, description);
        }

        private void NotifyStatusChange(ImportStage stage, int current, int total, int percentComplete, string description)
        {
            if (ImportStatusChange != null)
                ImportStatusChange(stage, current, total, percentComplete, description);
        }
        
        /// <summary>
        /// Gets all the files for a given project, optionally downloads them to a local folder
        /// </summary>
        private void ExportFiles(BasecampProject project, KiiroImportSettings importSettings)
        {
            XmlNodeList nodes = null;
            XmlNodeList dateGroupNodes = null;
            project.ProjectFiles = new List<BasecampFile>();
            int pageNum = 0;
            int currentFile = 0;
            int fileCount = -1;
            string localPath = importSettings.LocalCacheFolder;

            CookieAwareWebClient webClient = new CookieAwareWebClient();            
            webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);
            webClient.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(DownloadFileCompleted);            

            //set up the cache area if we are downloading files
            try
            {

                if (localPath.Substring(localPath.Length - 1, 1) != @"\")
                    localPath += @"\";                                                           
                localPath += "ProjectFileCache";
                if (!Directory.Exists(localPath))            
                    Directory.CreateDirectory(localPath);

                localPath += @"\" + project.ID + @"\";                    
                if (Directory.Exists(localPath) && !importSettings.ReuseFiles)
                {
                    Directory.Delete(localPath, true);
                }

                if (!Directory.Exists(localPath))
                    Directory.CreateDirectory(localPath);
                else
                    //clear out any long files. This avoids complexity when having to truncate file names and we're not sure
                    //if the file in the cache was downlaoded previously or in this session. Probably some smart logic could 
                    //be implemented here so we don't have to do this. Will re-visit if customers are having issues. Potentially 
                    //truncate the files on the web service rather than here.
                    DeleteLongFilesInCache(localPath);
                

            }
            catch(Exception ex)
            {
                throw new Exception("Could not setup local cache folder at " + localPath + ".", ex);
            }

            //loop through the different pages of files in basecamp and get the info
            do
            {


                ++pageNum;
                string pageURL = "projects/" + project.ID + "/files?page=" + pageNum;
                XmlDocument page = null;
                try
                {
                    page = GetBasecampPage(pageURL, importSettings);
                }
                catch (Exception ex)
                {
                    throw new Exception("Could not download Basecamp page " + pageURL, ex);
                }

        
                //determine the total file count
                if (fileCount == -1)
                {
                    //get the pagination section
                    nodes = page.SelectNodes("//div[@class='pagination']/span");
                    if (nodes.Count == 0)
                    {
                        //no pagination section - only one page. Count the files
                        nodes = page.SelectNodes("//div[starts-with(@class,'file_info')]/h1/a");                                
                        fileCount = nodes.Count;
                    }
                    else
                    {
                        //Extract the total files. Format is (Showing X of Y files)
                        string temp = nodes[0].InnerText;
                        temp = temp.Substring(temp.IndexOf("of") + 3);
                        temp = temp.Substring(0, temp.IndexOf(" files")).Trim();
                        fileCount = Int32.Parse(temp);
                    }

                    NotifyStatusChange(ImportStage.DownloadingFiles, 0, fileCount);
                    RecordImportLog("File count in Basecamp determined to be : " + fileCount.ToString(), importSettings, true);
                }
              

                //determine if we have been sent back to page 1 even though we are retrieving a later page
                if (pageNum > 1)
                {
                    //check the pagination section of the page
                    nodes = page.SelectNodes("//div[@class='pagination']/strong");
                    if (nodes.Count == 0)
                        //only 1 page on this project
                        break;
                    else
                    {
                        if (nodes[0].InnerText == "1")
                        {
                            break;
                        }
                    }
                }

                //get the files for this page
                dateGroupNodes = page.SelectNodes("//div[@class='date_group']");
                BasecampFile file = null;
                DateTime createdDate = DateTime.Now;                
                string modifiedBy = "";
                string fileName = "";
                foreach(XmlNode dateGroupNode in dateGroupNodes)
                {
                    //get the created date for the group
                    createdDate = DateTime.Parse(dateGroupNode.SelectSingleNode("h2/span").InnerText);

                    nodes = dateGroupNode.SelectNodes("div/div/div[starts-with(@class,'file_info')]");

                    foreach (XmlNode fileNode in nodes)
                    {
                        ++currentFile;

                        //try and get the modified by
                        modifiedBy = fileNode.SelectSingleNode("h3").InnerText;
                        
                        modifiedBy = modifiedBy.Substring(modifiedBy.IndexOf(" by ") + 4, modifiedBy.Length - (modifiedBy.IndexOf(" by ") + 4));
                        modifiedBy = modifiedBy.Substring(0, modifiedBy.IndexOf("\n"));

                        NotifyStatusChange(ImportStage.DownloadingFiles, currentFile, fileCount);

                        fileName = fileNode.SelectSingleNode("h1/a").InnerText;
                        fileName = GetCorrectedFileName(localPath, fileName, importSettings);
                        file = new BasecampFile(fileNode.SelectSingleNode("h1/a").Attributes["href"].Value, fileName, createdDate, modifiedBy);
                        file.KiiroCreatedBy = MatchBasecampUserToKiiroByName(file.CreatedByName, project, importSettings);
                        
                        //prefix the basecamp address if its a relative URL
                        if (!file.URL.ToLower().StartsWith("http"))
                            file.URL = (importSettings.BasecampFullURL + file.URL).Replace("//", "/").Replace(":/", "://");

                        file.Name = FixFileExtension(file.Name, file.URL);

                        project.ProjectFiles.Add(file);

                        //determine if this is a message or comment attachment
                        if (fileNode.SelectNodes("p/a").Count == 1)
                        {
                            string messageID = fileNode.SelectNodes("p/a")[0].Attributes["href"].Value;
                            messageID = messageID.Substring(messageID.LastIndexOf("/") + 1, messageID.Length - (messageID.LastIndexOf("/") + 1));
                            if (messageID[0] == 'C')
                                file.CommentID = messageID.Substring(1, messageID.Length - 1);
                            else if (messageID[0] == 'P')
                                file.MessageID = messageID.Substring(1, messageID.Length - 1);
                        }

                        

                        //download the file if we have to
                        try
                        {
                            file.LocalPath = localPath + file.Name;
                            if (File.Exists(file.LocalPath) && !importSettings.ReuseFiles)
                            {
                                File.Delete(file.LocalPath);
                            }
                            if (!File.Exists(file.LocalPath))
                            {

                                //MakeDummyFile(file.LocalPath);
                                webClient.Cookies = BasecampSession;
                                webClient.DownloadFileAsync(new Uri(file.URL), file.LocalPath);
                                do
                                {

                                    NotifyStatusChange(ImportStage.DownloadingFile, currentFile, fileCount, downloadProgress, file.Name);
                                    

                                }
                                while (webClient.IsBusy);
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Could not download file from " + file.URL + " to " + localPath + file.Name, ex);
                        }
                    }                   
                }

            } while (true);

            
        }

        private string FixFileExtension(string fileName, string URL)
        { 
            
            //sometimes the filename scraped from Basecamp does not have an extension, but the URL on the page does. 
            //if this is the case, use the extension from the URL
            if (!fileName.Contains("."))
            {
                if (URL.Contains("."))
                    fileName += URL.Substring(URL.LastIndexOf('.'), URL.Length - URL.LastIndexOf('.'));
            }

            return fileName;


        }
        private void DeleteLongFilesInCache(string cacheFolder)
        {
            string[] files = Directory.GetFiles(cacheFolder);
            string justFileName = "";
            foreach (string file in files)
            {
                justFileName = file.Substring(file.LastIndexOf("\\") + 1, file.Length - file.LastIndexOf("\\")-1);
                if (justFileName.Length == 50)
                    File.Delete(file);
            }
        }

        private bool IsFileExtensionAllowed(string fileName, KiiroImportSettings importSettings)
        {
            bool allowed = false;
            string extension = "";

            if (importSettings.BlockedFileExtensions == null)
            {
                importSettings.BlockedFileExtensions = new Hashtable();
                importSettings.BlockedFileExtensions.Add(".ade","");
                importSettings.BlockedFileExtensions.Add(".adp","");
                importSettings.BlockedFileExtensions.Add(".app","");
                importSettings.BlockedFileExtensions.Add(".asa","");
                importSettings.BlockedFileExtensions.Add(".ashx","");
                importSettings.BlockedFileExtensions.Add(".asmx","");
                importSettings.BlockedFileExtensions.Add(".asp","");
                importSettings.BlockedFileExtensions.Add(".bas","");
                importSettings.BlockedFileExtensions.Add(".bat","");
                importSettings.BlockedFileExtensions.Add(".cdx","");
                importSettings.BlockedFileExtensions.Add(".cer","");
                importSettings.BlockedFileExtensions.Add(".chm","");
                importSettings.BlockedFileExtensions.Add(".class","");
                importSettings.BlockedFileExtensions.Add(".cmd","");
                importSettings.BlockedFileExtensions.Add(".com","");
                importSettings.BlockedFileExtensions.Add(".config","");
                importSettings.BlockedFileExtensions.Add(".cpl","");
                importSettings.BlockedFileExtensions.Add(".crt","");
                importSettings.BlockedFileExtensions.Add(".csh","");
                importSettings.BlockedFileExtensions.Add(".dll","");
                importSettings.BlockedFileExtensions.Add(".exe","");
                importSettings.BlockedFileExtensions.Add(".fxp","");
                importSettings.BlockedFileExtensions.Add(".hlp","");
                importSettings.BlockedFileExtensions.Add(".hta","");
                importSettings.BlockedFileExtensions.Add(".htr","");
                importSettings.BlockedFileExtensions.Add(".htw","");
                importSettings.BlockedFileExtensions.Add(".ida","");
                importSettings.BlockedFileExtensions.Add(".idc","");
                importSettings.BlockedFileExtensions.Add(".idq","");
                importSettings.BlockedFileExtensions.Add(".ins","");
                importSettings.BlockedFileExtensions.Add(".isp","");
                importSettings.BlockedFileExtensions.Add(".its","");
                importSettings.BlockedFileExtensions.Add(".jse","");
                importSettings.BlockedFileExtensions.Add(".ksh","");
                importSettings.BlockedFileExtensions.Add(".lnk","");
                importSettings.BlockedFileExtensions.Add(".mad","");
                importSettings.BlockedFileExtensions.Add(".maf","");
                importSettings.BlockedFileExtensions.Add(".mag","");
                importSettings.BlockedFileExtensions.Add(".mam","");
                importSettings.BlockedFileExtensions.Add(".maq","");
                importSettings.BlockedFileExtensions.Add(".mar","");
                importSettings.BlockedFileExtensions.Add(".mas","");
                importSettings.BlockedFileExtensions.Add(".mat","");
                importSettings.BlockedFileExtensions.Add(".mau","");
                importSettings.BlockedFileExtensions.Add(".mav","");
                importSettings.BlockedFileExtensions.Add(".maw","");
                importSettings.BlockedFileExtensions.Add(".mda","");
                importSettings.BlockedFileExtensions.Add(".mdb","");
                importSettings.BlockedFileExtensions.Add(".mde","");
                importSettings.BlockedFileExtensions.Add(".mdt","");
                importSettings.BlockedFileExtensions.Add(".mdw","");
                importSettings.BlockedFileExtensions.Add(".mdz","");
                importSettings.BlockedFileExtensions.Add(".msc","");
                importSettings.BlockedFileExtensions.Add(".msh","");
                importSettings.BlockedFileExtensions.Add(".msh1","");
                importSettings.BlockedFileExtensions.Add(".msh1xml","");
                importSettings.BlockedFileExtensions.Add(".msh2","");
                importSettings.BlockedFileExtensions.Add(".msh2xml","");
                importSettings.BlockedFileExtensions.Add(".mshxml","");
                importSettings.BlockedFileExtensions.Add(".msi","");
                importSettings.BlockedFileExtensions.Add(".msp","");
                importSettings.BlockedFileExtensions.Add(".mst","");
                importSettings.BlockedFileExtensions.Add(".ops","");
                importSettings.BlockedFileExtensions.Add(".pcd","");
                importSettings.BlockedFileExtensions.Add(".pif","");
                importSettings.BlockedFileExtensions.Add(".prf","");
                importSettings.BlockedFileExtensions.Add(".prg","");
                importSettings.BlockedFileExtensions.Add(".printer","");
                importSettings.BlockedFileExtensions.Add(".pst","");
                importSettings.BlockedFileExtensions.Add(".reg","");
                importSettings.BlockedFileExtensions.Add(".rem","");
                importSettings.BlockedFileExtensions.Add(".scf","");
                importSettings.BlockedFileExtensions.Add(".scr","");
                importSettings.BlockedFileExtensions.Add(".sct","");
                importSettings.BlockedFileExtensions.Add(".shb","");
                importSettings.BlockedFileExtensions.Add(".shs","");
                importSettings.BlockedFileExtensions.Add(".shtm","");
                importSettings.BlockedFileExtensions.Add(".shtml","");
                importSettings.BlockedFileExtensions.Add(".soap","");
                importSettings.BlockedFileExtensions.Add(".stm","");
                importSettings.BlockedFileExtensions.Add(".url","");
                importSettings.BlockedFileExtensions.Add(".vb","");
                importSettings.BlockedFileExtensions.Add(".vbe","");
                importSettings.BlockedFileExtensions.Add(".vbs","");
                importSettings.BlockedFileExtensions.Add(".ws","");
                importSettings.BlockedFileExtensions.Add(".wsc","");
                importSettings.BlockedFileExtensions.Add(".wsf","");
                importSettings.BlockedFileExtensions.Add(".wsh","");
            }

            if (fileName.LastIndexOf('.') > 0 )
            {
                extension = fileName.Substring(fileName.LastIndexOf('.'), fileName.Length - fileName.LastIndexOf('.'));
                allowed = !importSettings.BlockedFileExtensions.Contains(extension);
            }

            return allowed;

        }

        /// <summary>
        /// Adds a count to the filename if it already exists
        /// </summary>
        private string GetCorrectedFileName(string path, string fileName, KiiroImportSettings importSettings)
        {
            string correctFileName = fileName;
            string originalFileName = fileName;
            string extension = "";

            if (fileName.Length > 50)
            {
                
                //truncate to 50 including extension. Go with 5 chars for extension to cover office 2007 .docx etc
                if (fileName.LastIndexOf('.') >= fileName.Length - 5)
                {
                    extension = fileName.Substring(fileName.LastIndexOf('.'), fileName.Length - fileName.LastIndexOf('.'));
                    fileName = fileName.Substring(0, fileName.LastIndexOf('.'));
                }
                fileName = fileName.Substring(0, 50 - extension.Length);
                correctFileName = fileName + extension;                
                
                //check if the file exists. if it doesn, we know it was created in this session as we cleared
                //out any 50 length files at the beginning of this process.
                if (File.Exists(path + fileName + extension))
                {
                    //local file is not the same file. Add numbers to the file name until we find a unique one
                    int count = 1;                        
                    do
                    {
                        ++ count;
                        fileName = fileName.Substring(0, 50 - (extension.Length + count.ToString().Length + 3));

                        if (!File.Exists(path + fileName + " (" + count.ToString() + ")" + extension))
                            break;
                    } while (true);

                    //we should have a final filename with a count
                    correctFileName = fileName + " (" + count.ToString() + ")" + extension;
                }                

            }
            if (correctFileName != originalFileName)
               RecordImportLog("File name > 50 in length. Renamed '" + originalFileName + "' to '" + correctFileName + "'.", importSettings, true);

            return correctFileName;


        }

        private long GetFileLength(string filePath)
        {
            long length = 0;
            FileStream file = File.OpenRead(filePath);
            length = file.Length;
            file.Close();
            return length;
        }

        private void MakeDummyFile(string path)
        {

            FileStream file = File.Open(path, FileMode.Create);
            file.WriteByte(Byte.Parse("1"));
            file.Close();
        }

        private void DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            downloadProgress = 100;
        }

        private void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            downloadProgress = e.ProgressPercentage;
        }

                
       
        


        /// <summary>
        /// Gets the given basecamp page and returns it as an XML Document
        /// </summary>
        private XmlDocument GetBasecampPage(string relativeUrl, KiiroImportSettings importSettings)
        {
            XmlDocument doc = null;

            //log in if we have to
            if (LoginToBasecamp(importSettings))
            {

                //Access the content
                HttpWebRequest webRequest = WebRequest.Create(importSettings.BasecampFullURL + relativeUrl) as HttpWebRequest;
                webRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
                webRequest.CookieContainer = BasecampSession;
                webRequest.Timeout = 10000;
                WebResponse response = webRequest.GetResponse();
                doc = ConvertResponseToXML(response, importSettings);
                
            }

            return doc;

        }
       

        private XmlDocument ConvertResponseToXML(WebResponse response, KiiroImportSettings importSettings)
        {
            StreamReader responseReader = new StreamReader(response.GetResponseStream());

            //read the stream into sgml
            Sgml.SgmlReader sqmlReader = new Sgml.SgmlReader();
            sqmlReader.DocType = "HTML";
            sqmlReader.WhitespaceHandling = WhitespaceHandling.All;
            sqmlReader.CaseFolding = Sgml.CaseFolding.ToLower;
            sqmlReader.InputStream = responseReader;
            
            
     
            

            // create the document
            XmlDocument doc = new XmlDocument();
            doc.PreserveWhitespace = true;
            doc.XmlResolver = null;
            doc.Load(sqmlReader);
            doc.Save(importSettings.LocalCacheFolder + @"\current_page.xml");

            return doc;
        }

        /// <summary>
        /// Connects to Basecamp if not already connected using the connection params. Sets the global session variable
        /// </summary>
        /// <param name="connectionParams"></param>
        /// <returns></returns>
        private bool LoginToBasecamp(KiiroImportSettings importSettings)
        {

            bool success = true;

            if (BasecampSession == null)
            {
                success = false;
                string loginPage = "https://" + importSettings.BasecampURL + "login";
                
                //open the login screen and get the basecamp session coookie
                HttpWebRequest webRequest = WebRequest.Create(loginPage) as HttpWebRequest;
                webRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
                CookieContainer cookies = new CookieContainer();
                webRequest.CookieContainer = cookies;
                WebResponse response = webRequest.GetResponse();
                response.Close();

                //try and authenticate, use the login cookies
                webRequest = HttpWebRequest.Create(loginPage + "/authenticate") as HttpWebRequest;
                string postData = String.Format("user_name={0}&password={1}&remember_me=off", importSettings.BasecampLogin, importSettings.BasecampPassword);
                webRequest.Method = "POST";
                webRequest.ContentType = "application/x-www-form-urlencoded";
                webRequest.CookieContainer = cookies;
                StreamWriter requestWriter = new StreamWriter(webRequest.GetRequestStream());
                requestWriter.Write(postData);
                requestWriter.Close();
                response = webRequest.GetResponse();
                
                //if the response URI is still the login, we have failed
                if (response.ResponseUri.AbsoluteUri.ToUpper() != loginPage.ToUpper())
                {
                    //check the response for https. Means SSL is enabled for the company.
                    //if ssl is turned on, basecamp will redirect to https even if hitting http. Record this fact in the import settings
                    if (response.ResponseUri.Scheme == "https")
                        importSettings.BasecampSecureMode = true;

                    success = true;
                    BasecampSession = cookies;
                }

                response.Close();    
                
            }
            return (success);
            
        }

        #region IDisposable Members

        public void Dispose()
        {
            BasecampSession = null;
        }

        #endregion
    }
}
